<?php
/**
* @package   com_zoo
* @author    YOOtheme http://www.yootheme.com
* @copyright Copyright (C) YOOtheme GmbH
* @license   http://www.gnu.org/licenses/gpl-2.0.html GNU/GPLv2 only
*/

/*
   Class: ImportHelper
   The Helper Class for import
*/
class ImportHelper extends AppHelper {

	/*
		Function: import
			Import from xml file.

		Parameters:
			$json_file 			- The json file
			$import_frontpage 	- if true, frontpage will be imported
			$frontpage_params 	- frontpage params to import
			$import_categories 	- if true, categories will be imported
			$category_params 	- category params to import
			$element_assignment - the element assignment
			$type_select 		- the selected types

		Returns:
			Boolean - true on success
	*/
	public function import(
		$json_file,
		$import_frontpage = true,
		$import_categories = true,
		$element_assignment = array(),
		$types = array()) {

		// set_time_limit doesn't work in safe mode
		if (!ini_get('safe_mode')) {
			@set_time_limit(0);
		}

		if (JFile::exists($json_file) && $data = $this->app->data->create(JFile::read($json_file))) {

			// get application
			if ($application = $this->app->zoo->getApplication()) {

				// import frontpage
				if (isset($data['categories']) && isset($data['categories']['_root']) && $import_frontpage) {
					$this->_importFrontpage($application, $data['categories']['_root']);
				}

				// import categories
				$categories = array();
				if (isset($data['categories']) && count($data['categories']) && $import_categories) {
					$categories_to_import = $data['categories'];
					unset($categories_to_import['_root']);
					$categories = $this->_importCategories($application, $categories_to_import);
				}

				// import items
				if (isset($data['items'])) {
					$this->_importItems($application, $data['items'], $element_assignment, $types, $categories);
				}

				return true;

			}

			throw new ImportHelperException('No application to import too.');

		}

		throw new ImportHelperException('No valid json file.');

	}

	private function _importFrontpage(Application $application, $frontpage) {

		$application->description = $frontpage['description'];

		// set frontpage params
		$application->getParams()->set('content.', $frontpage['content']);

		// save application
		try {
			$this->app->table->application->save($application);
		} catch (AppException $e) {
			$this->app->error->raiseNotice(0, JText::_('Error Importing Frontpage').' ('.$e.')');
		}

	}

	private function _importCategories(Application $application, $categories = array()) {

		// init vars
		$db			   = $this->app->database;
		$table		   = $this->app->table->category;
		$category_vars = array_keys(get_class_vars('Category'));

		// first iteration: save category vars
		$category_objects = array();
		foreach ($categories as $alias => $category) {

			$category_obj = $this->app->object->create('Category');
			$category_obj->alias = $this->app->string->sluggify($alias);

			// set a valid category alias
			$category_obj->alias = $this->app->alias->category->getUniqueAlias(0, $category_obj->alias);

			// set category values
			foreach ($category as $property => $value) {
				if (in_array($property, $category_vars)) {
					$category_obj->$property = $value;
				}
			}
			$category_obj->parent = 0;
			$category_obj->application_id = $application->id;

			// set category content params
			$category_obj->getParams()->set('content.', $category['content']);

			$db->query('INSERT INTO '. ZOO_TABLE_CATEGORY . '(alias) VALUES ('.$db->quote($category_obj->alias).')');
			$category_obj->id = $db->insertid();

			// store category for second iteration
			$category_objects[$alias] = $category_obj;

		}

		// second iteration: set parent relationship
		foreach ($categories as $alias => $category) {

			// only save if parent is set
			if (isset($category_objects[$alias])) {
				if (!empty($category['parent']) && $category['parent'] != '_root') {
					$category_objects[$alias]->parent = $category_objects[$category['parent']]->id;
				}
			}

			// save the category
			try {
				$table->save($category_objects[$alias]);
			} catch (AppException $e) {
				$this->app->error->raiseNotice(0, JText::_('Error Importing Category').' ('.$e.')');
			}

		}

		return $category_objects;
	}

	private function _importItems(Application $application, $items = array(), $element_assignment = array(), $types = array(), $categories = array()) {

		// init vars
		$db		       = $this->app->database;
		$table         = $this->app->table->item;
		$comment_table = $this->app->table->comment;
		$item_vars	   = array_keys(get_class_vars('Item'));
		$comment_vars  = array_keys(get_class_vars('Comment'));
		$user_id	   = $this->app->user->get()->get('id');
		$app_types	   = $application->getTypes();
		$authors       = $this->app->data->create($db->queryObjectList('SELECT id, username, name FROM #__users', 'id'));

		// disconnect from comment save event
		$this->app->event->dispatcher->disconnect('comment:saved', array('CommentEvent', 'saved'));

		$item_objects = array();
		foreach ($items as $alias => $item) {

			if (isset($item['group']) && isset($types[$item['group']]) && !empty($types[$item['group']]) && ($type = $app_types[$types[$item['group']]])) {

				$elements = $type->getElements();

				$item_obj 		 	 = $this->app->object->create('Item');
				$item_obj->alias 	 = $this->app->string->sluggify($alias);
				$item_obj->type  	 = $type->id;

				// set a valid category alias
				$item_obj->alias = $this->app->alias->item->getUniqueAlias(0, $item_obj->alias);

				$db->query('INSERT INTO '. $table->name . '(alias) VALUES ('.$db->quote($item_obj->alias).')');
				$item_obj->id = $db->insertid();

				// set item values
				foreach ($item as $property => $value) {
					if (in_array($property, $item_vars)) {
						$item_obj->$property = $value;
					}
				}

				// fix access if j16
				if (!$this->app->joomla->isVersion('1.5')) {
					$item_obj->access = $item_obj->access == 0 ? $this->app->joomla->getDefaultAccess() : $item_obj->access;
				}

				// store application id
				$item_obj->application_id = $application->id;

				// store categories
				$item_obj->categories = array();
				if (isset($item['categories'])) {
					foreach ($item['categories'] as $category_alias) {
						$item_obj->categories[] = $category_alias;
					}
				}

				// store tags
				if (isset($item['tags'])) {
					$item_obj->setTags($item['tags']);
				}

				// store author
				$item_obj->created_by_alias = "";
				if (isset($item['author'])) {
					if ($key = $authors->searchRecursive($item['author'])) {
						$item_obj->created_by = (int) $authors[$key]->id;
					} else {
						$item_obj->created_by_alias = $item['author'];
					}
				}
				// if author is unknown set current user as author
				if (!$item_obj->created_by) {
					$item_obj->created_by = $user_id;
				}

				// store modified_by
				$item_obj->modified_by = $user_id;

				// store element_data
				$item_obj->elements = $this->app->data->create();
				if (isset($item['elements'])) {
					foreach ($item['elements'] as $old_element_alias => $element) {
						if (isset($element['data'])
								&& isset($element_assignment[$item['group']][$old_element_alias][$type->id])
								&& ($element_alias = $element_assignment[$item['group']][$old_element_alias][$type->id])
								&& ($element_obj = $item_obj->getElement($element_alias))) {

							$element_obj->bindData($element['data']);
						}
					}
				}

				// set metadata, content, config params
				$item_obj->getParams()->set('metadata.', $item['metadata']);
				$item_obj->getParams()->set('content.', $item['content']);
				$item_obj->getParams()->set('config.', $item['config']);

				$item_objects[$alias] = $item_obj;

				// save item -> category relationship
				if (!empty($categories) && isset($item['categories'])) {

					if (isset($item['config']['primary_category']) && isset($categories[$item['config']['primary_category']])) {
						$item_obj->getParams()->set('config.primary_category', $categories[$item['config']['primary_category']]->id);
					}

					$item_categories = array();
					foreach ($item['categories'] as $category_alias) {
						if (isset($categories[$category_alias]) || $category_alias == '_root') {
							$item_categories[] = $category_alias == '_root' ? 0 : (int) $categories[$category_alias]->id;
						}
					}

					if (!empty($item_categories)) {
						$this->app->category->saveCategoryItemRelations($item_obj->id, $item_categories);
					}
				}

				// save comments
				if (isset($item['comments']) && is_array($item['comments'])) {
					foreach ($item['comments'] as $key => $comment) {
						$comment_obj = $this->app->object->create('comment');
						$comment_obj->item_id = $item_obj->id;

						// set item values
						foreach ($comment as $property => $value) {
							if (in_array($property, $comment_vars)) {
								$comment_obj->$property = $value;
							}
						}

						if (isset($comment_obj->user_type) && $comment_obj->user_type == 'joomla') {
							if (isset($comment['username']) && ($key = $authors->searchRecursive($comment['username']))) {
								$comment_obj->user_id = (int) $authors[$key]->id;
								$comment_obj->author = (string) $authors[$key]->name;
							} else {
								$comment_obj->user_id = $comment_obj->user_type = '';
							}
						}
						$comment_table->save($comment_obj);
						$item['comments'][$key] = $comment_obj;
					}
					// sanatize parent ids
					foreach ($item['comments'] as $key => $comment) {
						if ($comment->parent_id && isset($item['comments'][$comment->parent_id])) {
							$comment->parent_id = $item['comments'][$comment->parent_id]->id;
							$comment_table->save($comment);
						}
					}
				}
			}
		}

		foreach ($item_objects as $item) {

			foreach ($item->getElements() as $element) {

				// sanatize relateditems elements
				if ($element->getElementType() == 'relateditems') {
					$relateditems = $element->get('item', array());
					$new_related_items = array();
					foreach ($relateditems as $relateditem) {
						if (isset($items[$relateditem])) {
							$new_related_items[] = $item_objects[$relateditem]->id;
						}
					}
					$element->set('item', $new_related_items);

				// sanitize relatedcategories elements aliases
				} else if ($element->getElementType() == 'relatedcategories') {
					$relatedcategories = $element->get('category', array());
					$new_related_categories = array();
					foreach ($relatedcategories as $relatedcategory) {
						if (isset($categories[$relatedcategory])) {
							$new_related_categories[] = $categories[$relatedcategory]->id;
						}
					}
					$element->set('category', $new_related_categories);
				}
			}

			try {

				$table->save($item);

			} catch (AppException $e) {
				$this->app->error->raiseNotice(0, JText::_('Error Importing Item').' ('.$e.')');
			}

		}

		return $item_objects;

	}

	/*
		Function: getImportInfo
			Builds the assign element info from xml.

		Parameters:
			$data - AppData: the export data

		Returns:
			Array - Assign element info
	*/
	public function getImportInfo(AppData $data) {

		$info = array();

		$application = $this->app->zoo->getApplication();

		// get frontpage count
		$info['frontpage_count'] = (bool) $data->find('categories._root');

		// get category count
		$info['category_count'] = max(array(count($data->get('categories')) - ((int) $info['frontpage_count']), 0));

		// get types
		foreach ($application->getTypes() as $type) {
			foreach ($type->getElements() as $element) {
				$type_elements[$type->id][$element->getElementType()][] = $element;
			}
		}

		// get item types
		$info['items'] = array();
		foreach ($data['items'] as $alias => $item) {
			$group = $item['group'];
			if (!isset($info['items'][$group])) {
				$info['items'][$group]['item_count'] = 0;
				$info['items'][$group]['elements'] = array();
				if (isset($item['elements'])) {
					foreach ($item['elements'] as $alias => $element) {
						if (!isset($info['items'][$group]['elements'][$alias])){

							// add element type
							$info['items'][$group]['elements'][$alias]['type'] = ucfirst($element['type']);

							// add element name
							$info['items'][$group]['elements'][$alias]['name'] = $element['name'];

							// add elements to assign too
							$info['items'][$group]['elements'][$alias]['assign'] = array();
							foreach ($type_elements as $type => $assign_elements) {
								if (isset($assign_elements[$element['type']])) {
									$info['items'][$group]['elements'][$alias]['assign'][$type] = $assign_elements[$element['type']];
								}
							}
						}
					}
				}
			}
			$info['items'][$group]['item_count'] += 1;
		}

		return $info;
	}

	/*
		Function: import
			Import from xml file.

		Parameters:
			$file				- The csv file
			$type			 	- the type to import to
			$contains_headers 	- does the csv file contain a header row
			$field_separator 	- the field separator
			$field_enclosure 	- the field enclosure
			$element_assignment - the element assignment

		Returns:
			Boolean - true on success
	*/
	public function importCSV(
		$file,
		$type = '',
		$contains_headers = false,
		$field_separator = ',',
		$field_enclosure = '"',
		$element_assignment = array()) {

		// set_time_limit doesn't work in safe mode
		if (!ini_get('safe_mode')) {
			@set_time_limit(0);
		}

		// get application
		if ($application = $this->app->zoo->getApplication()) {

			if ($type_obj = $application->getType($type)) {

				$c = 0;
				$assignments = array();
				foreach ($element_assignment as $column => $value) {
					if (!empty($value[$type])) {
						$name = $value[$type];
						$assignments[$name][] = $column;
					}
				}

				if (!isset($assignments['_name'])) {
					throw new ImportHelperException('No item name was assigned.');
				}

				// make sure the line endings are recognized irrespective of the OS
				ini_set('auto_detect_line_endings', true);

				if (($handle = fopen($file, "r")) !== FALSE) {

					$item_table			= $this->app->table->item;
					$category_table		= $this->app->table->category;
					$user_id			= $this->app->user->get()->get('id');
					$now				= $this->app->date->create();
					$row				= 0;
					$app_categories		= $application->getCategories();
					$app_categories		= array_map(create_function('$cat', 'return $cat->name;'), $app_categories);

					while (($data = fgetcsv($handle, 0, $field_separator, $field_enclosure)) !== FALSE) {
						if (!($contains_headers && $row == 0)) {

							$item = $this->app->object->create('Item');
							$item->application_id = $application->id;
							$item->type = $type;

							// set access
							$item->access = $this->app->joomla->getDefaultAccess();

							// store created by
							$item->created_by  = $user_id;

							// set created
							$item->created	   = $now->toMySQL();

							// store modified_by
							$item->modified_by = $user_id;

							// set modified
							$item->modified	   = $now->toMySQL();

							// store element_data and item name
							$item_categories = array();

							$elements = $item->getElements();
							foreach ($assignments as $assignment => $columns) {
								$column = current($columns);
								switch ($assignment) {
									case '_name':
										$item->name = $data[$column];
										break;
									case '_created_by_alias':
										$item->created_by_alias = $data[$column];
										break;
									case '_created':
										if (!empty($data[$column])) {
											$item->created = $data[$column];
										}
										break;
									default:
										if (substr($assignment, 0, 9) == '_category') {
											foreach ($columns as $column) {
												$item_categories[] = $data[$column];
											}
										} else if (isset($elements[$assignment])) {
											switch ($elements[$assignment]->getElementType()) {
												case 'text':
												case 'textarea':
												case 'link':
												case 'email':
												case 'date':
													$element_data = array();
													foreach ($columns as $column) {
														if (!empty($data[$column])) {
															$element_data[$column] = array('value' => $data[$column]);
														}
													}
													$elements[$assignment]->bindData($element_data);
													break;
												case 'gallery':
													$data[$column] = trim($data[$column], '/\\');
													$elements[$assignment]->bindData(array('value' => $data[$column]));
													break;
												case 'image':
												case 'download':
													$elements[$assignment]->bindData(array('file' => $data[$column]));
													break;
												case 'googlemaps':
													$elements[$assignment]->bindData(array('location' => $data[$column]));
													break;
											}
										}
										break;
								}
							}

							$item->alias = $this->app->string->sluggify($item->name);

							if (empty($item->alias)) {
								$item->alias = '42';
							}

							// set a valid category alias
							$item->alias = $this->app->alias->item->getUniqueAlias(0, $item->alias);

							if (!empty($item->name)) {

								try {

									$item_table->save($item);
									$item_id = $item->id;

									// store categories
									$related_categories = array();
									foreach ($item_categories as $category_name) {

										if (!in_array($category_name, $app_categories)) {

											$category = $this->app->object->create('Category');
											$category->application_id = $application->id;
											$category->name = $category_name;
											$category->parent = 0;

											$category->alias = $this->app->string->sluggify($category_name);

											// set a valid category alias
											$category->alias = $this->app->alias->category->getUniqueAlias($category->alias);

											try {

												$category_table->save($category);
												$related_categories[] = $category->id;
												$app_categories[$category->id] = $category->name;

											} catch (CategoryTableException $e) {}

										} else {

											$related_categories[] = array_search($category_name, $app_categories);

										}
									}

									// add category to item relations
									if (!empty($related_categories)) {

										$this->app->category->saveCategoryItemRelations($item_id, $related_categories);

									}

								} catch (ItemTableException $e) {}
							}
						}

						$row++;
					}
					fclose($handle);
					return true;

				} else {
					throw new ImportHelperException('Could not open csv file.');
				}
			} else {
				throw new ImportHelperException('Could not find type.');
			}
		}

		throw new ImportHelperException('No application to import too.');

	}

	/*
		Function: getImportInfoCSV
			Builds the assign element info from csv.

		Parameters:
			$file - CSV file

		Returns:
			Array - Assign element info
	*/
	public function getImportInfoCSV($file, $contains_headers = false, $field_separator = ',', $field_enclosure = '"') {

		$info = array();

		$application = $this->app->zoo->getApplication();

		// get types
		$info['types'] = array();
		foreach ($application->getTypes() as $type) {
			$info['types'][$type->id] = array();
			foreach ($type->getElements() as $element) {
				// filter elements
				if (in_array($element->getElementType(), array('text', 'textarea', 'link', 'email', 'image', 'gallery', 'download', 'date', 'googlemaps'))) {
					$info['types'][$type->id][$element->getElementType()][] = $element;
				}
			}
		}

		// get item types
		$info['item_count'] = 0;

		$info['columns'] = array();

		// make sure the line endings are recognized irrespective of the OS
		ini_set('auto_detect_line_endings', true);

		// get column names and row count
		$row = 0;
		$columns = 0;
		if (($handle = fopen($file, "r")) !== FALSE) {

			while (($data = fgetcsv($handle, 0, $field_separator, $field_enclosure)) !== FALSE) {
				if ($row == 0) {
					// get column names from header row
					if ($contains_headers) {
						$info['columns'] = $data;
					} else {
						$info['columns'] = array_fill(0, count($data), '');
					}
				}

				// get max column count
				$row++;

			}

			// get item count
			$info['item_count'] = $contains_headers ? $row - 1 : $row;

			fclose($handle);
		}

		return $info;
	}

}

/*
	Class: ImportHelperException
*/
class ImportHelperException extends AppException {}